//
//  UIKit+EC.m
//  ECDevelopKit
//
//  Created by LittoCats on 8/7/14.
//  Copyright (c) 2014 Littocats. All rights reserved.
//

#import "UIKit+ECS.h"
#import "NSObject+ECS.h"
#import "ECSScriptContext.h"
#import "ECSObjectAttributeWrapper.h"
#import <objc/runtime.h>
#import <objc/message.h>

//******************************************************************************************//

@implementation UIApplication (ECS)

@dynamic ecsContext;
- (ECSScriptContext *)ecsContext
{
    static ECSScriptContext *context = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        context = [ECSScriptContext context];
    });
    return context;
}

@dynamic ecsCache;
- (NSMapTable *)ecsCache
{
    static NSMapTable *cache = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        cache = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsWeakMemory capacity:64];
    });
    return cache;
}
@end

//******************************************************************************************//



//******************************************************************************************//

@implementation UIView (ECS)

@dynamic ecsAttributes;
static const char *kECUIViewCacheKey;
- (NSMutableDictionary *)ecsAttributes
{
    NSMutableDictionary *cache = objc_getAssociatedObject(self, &kECUIViewCacheKey);
    if (!cache) {
        cache = [NSMutableDictionary new];
        objc_setAssociatedObject(self, &kECUIViewCacheKey, cache, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    return cache;
}

/**
 *  实现 becomeFirstResponder 方法，触发该方法时，若 self 不是 firstResponder ，则 resign current firstResponder
 */
- (BOOL)becomeFirstResponder
{
    [super becomeFirstResponder];
    UIResponder *current = [[[UIApplication sharedApplication] ecsCache] objectForKey:@"_______firstResponder"];
    if (current != self) {
        [current resignFirstResponder];
        [[[UIApplication sharedApplication] ecsCache] setObject:self forKey:@"_______firstResponder"];
    }
    return YES;
}

@end

//******************************************************************************************//

#import <AssetsLibrary/AssetsLibrary.h>

@implementation UIColor (ECS)

+ (UIColor *)colorWithScript:(NSString *)script
{
    if (!script || [script isEmpty])  return nil;
    
    if ([script hasPrefix:@"#"]) {
        return [self colorWithHexString:script];
    }else if ([script hasPrefix:@"@"]){
        return [self colorWithPatternImage:[UIImage imageWithScript:[script substringFromIndex:1]]];
    }else if ([script hasPrefix:@"("] && [script hasSuffix:@")"]){
        return [self colorWithARGBScript:script];
    }else{
        SEL selector = NSSelectorFromString([script stringByAppendingString:@"Color"]);
        if ([self respondsToSelector:selector]) {
            return  objc_msgSend(self, selector);
        }
    }
    return [UIColor whiteColor];
}

- (ECSHexString *)script
{
    CGFloat red,green,blue,alpha;
    [self getRed:&red green:&green blue:&blue alpha:&alpha];
    return [[ECSHexString alloc] initWithFormat:@"#%.2X%.2X%.2X%.2X",(int)(red*255),(int)(green*255),(int)(blue*255),(int)(alpha*255)];
}

#pragma mark- private
+ (UIColor*)colorWithHexString:(ECSHexString *)hexString{
    NSString *colorString = [[hexString substringFromIndex:1] uppercaseString];
    CGFloat alpha, red, blue, green;
    char *p;
    NSUInteger hexValue = strtoul([colorString cStringUsingEncoding:NSUTF8StringEncoding], &p, 16);
    switch ([colorString length]) {
        case 3: // #RGB
            alpha = 255;
            red = ((hexValue & 0xf00) >> 8);
            green = ((hexValue & 0xf0) >> 4);
            blue = (hexValue & 0xf);
            break;
        case 4: // #ARGB
            alpha = ((hexValue & 0xf000) >> 12);
            red = ((hexValue & 0xf00) >> 8);
            green = ((hexValue & 0xf0) >> 4);
            blue = (hexValue & 0xf);
            break;
        case 6: // #RRGGBB
            alpha = 255;
            red = ((hexValue & 0xff0000) >> 16);
            green = ((hexValue & 0xff00) >> 8);
            blue = (hexValue & 0xff);
            break;
        case 8: // #AARRGGBB
            alpha = ((hexValue & 0xff000000) >> 24);
            red = ((hexValue & 0xff0000) >> 16);
            green = ((hexValue & 0xff00) >> 8);
            blue = (hexValue & 0xff);
            break;
        default:
            [NSException raise:@"Invalid color value" format: @"Color value %@ is invalid.  It should be a hex value of the form #RGB, #ARGB, #RRGGBB, or #AARRGGBB", hexString];
            break;
    }
    return [self colorWithRed: red/255.0 green: green/255.0 blue: blue/255.0 alpha: alpha/255.0];
}

+ (UIColor *)colorWithARGBScript:(NSString *)script
{
    NSArray *componment = [[script substringWithRange:NSMakeRange(1, script.length - 2)] componentsSeparatedByString:@","];
    CGFloat alpha, red, blue, green;
    red = componment.count > 0 ? [componment[0] integerValue] : 255;
    green = componment.count > 1 ? [componment[1] integerValue] : 255;
    blue = componment.count > 2 ? [componment[2] integerValue] : 255;
    alpha = componment.count > 3 ? [componment[3] integerValue] : 255;
    
    return [self colorWithRed: red/255.0 green: green/255.0 blue: blue/255.0 alpha: alpha/255.0];
}
+ (UIColor *)randomColor {
    CGFloat hue = ( arc4random() % 256 / 256.0 );  //  0.0 to 1.0
    CGFloat saturation = ( arc4random() % 128 / 256.0 ) + 0.5;  //  0.5 to 1.0, away from white
    CGFloat brightness = ( arc4random() % 128 / 256.0 ) + 0.5;  //  0.5 to 1.0, away from black
    return [UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:1];
}

- (UIColor *)brightColor:(CGFloat)rate
{
    CGFloat red,green,blue,alpha;
    [self getRed:&red green:&green blue:&blue alpha:&alpha];
    
    rate = rate >= 1 ? 1 : rate <= -1 ? -1 : rate;
    
    CGFloat refrenceValue = rate > 0;
    rate = fabsf(rate);
    
    red += (refrenceValue-red)*rate;
    green += (refrenceValue-green)*rate;
    blue += (refrenceValue-blue)*rate;
    
    return [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
}
@end

//******************************************************************************************//

@implementation UIImage (ECS)

+ (instancetype)imageWithScript:(NSString *)script
{
    return [UIImage imageNamed:script];
}

+ (instancetype)imageWithScript:(NSString *)script context:(id)context
{
    if ([script rangeOfString:@"/"].location != NSNotFound) {
        //script 为文件路径
        NSString *path = script;
        if ([path hasPrefix:@"~"]) path = [path stringByResolvingSymlinksInPath];
        else if ([path hasPrefix:@"."]) path = [[ECS_WrappedObjectAttribute(ECS_BoxedAttribute(context, [ECSScriptContext class]), ECSContextPathKey) stringByAppendingPathComponent:path] stringByResolvingSymlinksInPath];
        return [UIImage imageWithContentsOfFile:path];
    }
    return [UIImage imageNamed:script];
}

+ (void)imageWithScript:(NSString *)script context:(id)context completionHandler:(void (^)(NSString *, UIImage *))completionHandler
{
    UIImage *image = [self ecsDeCacheImageWithScript:script];
    if (image){
        if (completionHandler)
            [[self ecsImageLoadQueue] addOperationWithBlock:^{
                completionHandler(script, image);
            }];
        return;
    }
    //非远程图片
    if ([script rangeOfString:@"[a-zA-z]+://[^\\s]*" options:NSRegularExpressionSearch].location == NSNotFound){
        UIImage *image = [self imageWithScript:script context:context];
        [self ecsDispatchImage:image withScript:script toHandler:completionHandler];
        return;
    }

    //远程图片
    NSURL *url = [NSURL URLWithString:script];
    
    //image library
    if ([url.scheme isEqualToString:@"assets-library"]) {
        static ALAssetsLibrary* imageLibrary;
        if (!imageLibrary) imageLibrary = [[ALAssetsLibrary alloc] init];
        [imageLibrary assetForURL:url
                      resultBlock:^(ALAsset *asset) {
                          UIImage *image = [UIImage imageWithCGImage:[[asset defaultRepresentation] fullResolutionImage]];
                          [self ecsDispatchImage:image withScript:script toHandler:completionHandler];
                      } failureBlock:^(NSError *error) {
                          [self ecsDispatchImage:nil withScript:script toHandler:completionHandler];
                      }];
    }else if ([[url.scheme lowercaseString] isEqualToString:@"http"]){
        NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
        [request setHTTPMethod:@"GET"];
        [request setTimeoutInterval:60];//超时时间
        [request setURL:url];
//        [NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:url.host];// Private API
        
        [NSURLConnection sendAsynchronousRequest:request
                                           queue:[UIImage ecsImageLoadQueue]
                               completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
                                   UIImage *image;
                                   if (!connectionError && [response.MIMEType hasPrefix:@"image"]){
                                       image = [[UIImage alloc] initWithData:data];
                                   }
                                   [self ecsDispatchImage:image withScript:script toHandler:completionHandler];
                               }];
    }
}

+ (void)ecsDispatchImage:(UIImage *)image
             withScript:(NSString *)script
              toHandler:(void (^)(NSString *, UIImage *))completionHandler
{
    if (image && script) [self ecsCacheImage:image withScript:script];
    if (completionHandler)
        dispatch_sync(dispatch_get_main_queue(), ^{
            completionHandler(script, image);
        });
}

#pragma mark- image cache
+ (NSOperationQueue *)ecsImageLoadQueue
{
    static NSOperationQueue *queue = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        queue = [[NSOperationQueue alloc] init];
        [queue setMaxConcurrentOperationCount:NSOperationQueueDefaultMaxConcurrentOperationCount];
    });
    return queue;
}

+ (void)ecsCacheImage:(UIImage *)image withScript:(NSString *)script
{
    if (!image) return;

    [[self ecsDefaultImageCache] setObject:image forKey:script];
    [image writeToCacheFile:script];
}

+ (UIImage *)ecsDeCacheImageWithScript:(NSString *)script
{
    UIImage *image = [[self ecsDefaultImageCache] objectForKey:script];
    if (!image){
        image = [UIImage imageWithContentsOfFile:[NSString stringWithFormat:@"%@/ecImageCache/%@", NSTemporaryDirectory(),script.MD5]];
        image ? [[self ecsDefaultImageCache] setObject:image forKey:script] : nil;
    }
    return image;
}

+ (NSCache *)ecsDefaultImageCache
{
    static NSCache *cache = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        cache = [[NSCache alloc] init];
    });
    return cache;
}

+ (NSString *)ecsImageCachePath
{
    static NSString *cachePath = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        cachePath = [NSString stringWithFormat:@"%@ecImageCache/", NSTemporaryDirectory()];
    });
    return cachePath;
}

+ (void)clearImageCache
{
    [[self ecsDefaultImageCache] removeAllObjects];
    [[NSFileManager defaultManager]removeItemAtPath:[self ecsImageCachePath] error:nil];
}

- (void)writeToCacheFile:(NSString *)fileName
{
    dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        [[NSFileManager defaultManager] createDirectoryAtPath:[UIImage ecsImageCachePath] withIntermediateDirectories:YES attributes:nil error:nil];
        [UIImagePNGRepresentation(self) writeToFile:[[UIImage ecsImageCachePath] stringByAppendingString:fileName.MD5] atomically:YES];
    });
}
@end

//******************************************************************************************//

@implementation UIFont (ECS)

+ (UIFont *)fontWithScript:(NSString *)script
{
    NSArray *fontScript = [script componentsSeparatedByString:@" "];
    
    NSString *fontName = [fontScript firstObject];
    CGFloat fontSize = [[fontScript lastObject] floatValue];
    fontSize = fontSize > 0.0 ? fontSize : 13;
    
    //Can't find font with fontName , use system font
    if (![[UIFont familyNames] containsObject:fontName])
        return [UIFont systemFontOfSize:fontSize];
    
    
    return [UIFont fontWithName:fontName size:fontSize];
}

- (NSString *)script
{
    return [NSString stringWithFormat:@"%@ %f",self.fontName, self.pointSize];
}
@end


//******************************************************************************************//


@implementation UIImageView (ECS)

- (void)setImageWithScript:(NSString *)script
{
    [self setImageWithScript:script placeHolder:nil];
}
- (void)setImageWithScript:(NSString *)script
               placeHolder:(UIImage *)placeHolder
{
    NSString *_id = self._id;
    [self setImageWithScript:script placeHolder:placeHolder completionHandler:^(NSString *script, UIImage *image) {
        if (image && [image isKindOfClass:[UIImage class]])
            [[NSObject findObjectById:_id] setImage:image];
    }];
}

- (void)setImageWithScript:(NSString *)script
               placeHolder:(UIImage *)placeHolder
         completionHandler:(void (^)(NSString *, UIImage *))completionHandler
{
    NSAssert([placeHolder isKindOfClass:[UIImage class]] || !placeHolder, @"UIImageView's placeHolder must be UIImage object or nil.");
    [self setImage:placeHolder];
    
    //显示loading框
    UIActivityIndicatorView *aiv = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
    [aiv setColor:[UIColor darkGrayColor]];
    [aiv setCenter:CGPointMake(self.frame.size.width/2, self.frame.size.height/2)];
    [self addSubview:aiv];
    [aiv startAnimating];
    
    NSString *_id = aiv._id;
    [UIImage imageWithScript:script context:self completionHandler:^(NSString *script, UIImage *image) {
        UIActivityIndicatorView *aiv = [NSObject findObjectById:_id];
        [aiv stopAnimating];
        [aiv removeFromSuperview];
        if (completionHandler) completionHandler(script, image);
    }];
}

@end
